﻿using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

namespace TCG
{
	/// <summary>
	/// 材质数据库，加载材质、保存材质信息等.
	/// </summary>

    public class GameMaterialDatabase : BaseAssetsDatabase<Material> 
	{
		public override void UnloadUnusedAssets ()
		{
			Debug.Log ("GameMaterialDatabase: UnloadUnusedAssets");
			foreach (KeyValuePair<uint, int> _pair in m_zeroRefAssets)
			{
				uint assetId = _pair.Key;
				GameAsset<Material> materialAsset = m_cachedAssets[assetId];
				Debug.Log ("* " + materialAsset.ToString ());

				Material mat = materialAsset.asset;
				GameObject.DestroyImmediate (mat, true);
				m_cachedAssets.Remove (assetId);

				MaterialData data = m_cachedAssetsDepends[assetId];
				data.Unload ();
				m_cachedAssetsDepends.Remove (assetId);
			}
			m_zeroRefAssets.Clear ();
		}

        protected override string GetMapFilePath()
        {
            return CommonTools.GetStreamingPath ("config/material_map");
        }

        protected override void DoLoad (AssetRequest request)
        {
			m_assetLoader.LoadProtobufStream<MaterialData> (request, LoadCallback);
        }

		void LoadCallback (AssetRequest request, MaterialData matData)
		{
			if (!string.IsNullOrEmpty (request.error))
			{
				OnLoadFail (request.assetId, request.error);
			}
			else
			{
				MaterialMaker mm = new MaterialMaker (request, matData, MakeMaterialCallback);
				m_dependencyRequesters.Add (request.assetId, mm);
				mm.MakeMaterial ();
			}
		}

		void MakeMaterialCallback (AssetRequest request, Material mat, MaterialData materialData)
		{
			if (string.IsNullOrEmpty (request.error))
			{
				OnLoadSuccess (request.assetId, mat);
				m_cachedAssetsDepends.Add (request.assetId, materialData);
			}
			else
			{
				OnLoadFail (request.assetId, request.error);
			}
			m_dependencyRequesters.Remove (request.assetId);
		}

		/// <summary>
		/// 加载材质时记录其相依赖的贴图列表请求
		/// </summary>

		class MaterialMaker
		{
			public MaterialMaker (AssetRequest request, MaterialData matData, Action<AssetRequest, Material, MaterialData> callback)
			{
				m_request= request;
				m_matData = matData;
				m_makeCallback = callback;
			}

			public void MakeMaterial ()
			{
				IList<TextureInMat> texturesInMat = m_matData.textures;
				if (texturesInMat != null && texturesInMat.Count > 0)
				{
					for (int i = 0, len = texturesInMat.Count;i < len; ++ i)
					{
						TextureInMat textureInMat = texturesInMat[i];
						new AssetRequest (textureInMat.textureId, AssetType.UnityAssetBundle, OnTextureResponse).Request<Texture> ();
					}
				}
				else
				{
					AllDependenciesCollected ();
				}
			}

			void OnTextureResponse (AssetRequest request)
			{
				// 材质是使用了多次同一贴图时，这里要防止同一贴图多次加载成功回调引起的问题
				if (allTexturesLoaded) return;

				IList<TextureInMat> texturesInMat = m_matData.textures;

				if (!string.IsNullOrEmpty (request.error))
				{
					// 加载出现错误时，将之前加载好的贴图引用减1
					for (int i = 0, len = texturesInMat.Count; i < len; ++ i)
					{
						GameAsset<Texture> textureAsset = texturesInMat[i].textureAsset;
						if (textureAsset != null)
						{
							textureAsset.DecRef ();
						}
					}

					m_request.error = request.error;
					m_makeCallback (m_request, null, m_matData);
					return;
				}

				// 检查全部贴图是否都已加载成功，成功的话构建材质
				allTexturesLoaded = true;
				for (int i = 0, len = texturesInMat.Count; i < len; ++ i)
				{
					TextureInMat textureInMat = texturesInMat[i];
					if (textureInMat.textureId == request.assetId)
					{
						textureInMat.textureAsset = request.GetResultAsset<Texture> ();
					}

					allTexturesLoaded = allTexturesLoaded && (textureInMat.textureAsset != null);
				}
				if (allTexturesLoaded)
				{
					AllDependenciesCollected ();
				}
			}

			void AllDependenciesCollected ()
			{	
				Material newMat = new Material (Shader.Find (m_matData.shaderName));
				newMat.name = m_matData.matName;

				// setup textures
				IList<TextureInMat> texturesInMat = m_matData.textures;
				if (texturesInMat != null && texturesInMat.Count > 0)
				{
					for (int i = 0, len = texturesInMat.Count; i <len; ++ i)
					{
						TextureInMat textureInMat = texturesInMat[i];
						newMat.SetTexture (textureInMat.propertyName, textureInMat.textureAsset.asset);
						newMat.SetTextureOffset (textureInMat.propertyName, new Vector2 (textureInMat.textureOffsetX, textureInMat.textureOffsetY));
					}
				}
				
				// setup colors
				IList<ColorInMat> colorsInMat = m_matData.colors;
				if (colorsInMat != null && colorsInMat.Count > 0)
				{
					for (int i = 0, len = colorsInMat.Count; i < len; ++ i)
					{
						ColorInMat colorInMat = colorsInMat[i];
						newMat.SetColor (colorInMat.propertyName, new Color (colorInMat.r, colorInMat.g, colorInMat.b, colorInMat.a));
					}
				}
				
				// setup floats
				IList<FloatInMat> floatsInMat = m_matData.floats;
				if (floatsInMat != null && floatsInMat.Count > 0)
				{
					for (int i = 0, len = floatsInMat.Count; i < len; ++ i)
					{
						FloatInMat floatInMat = floatsInMat[i];
						newMat.SetFloat (floatInMat.propertyName, floatInMat.floatValue);
					}
				}
				
				// setup vectors
				IList<VectorInMat> vectorsInMat = m_matData.vectors;
				if (vectorsInMat != null && vectorsInMat.Count > 0)
				{
					for (int i = 0, len = vectorsInMat.Count; i < len; ++ i)
					{
						VectorInMat vectorInMat = vectorsInMat[i];
						newMat.SetVector (vectorInMat.propertyName, new Vector4 (vectorInMat.x, vectorInMat.y, vectorInMat.z, vectorInMat.z));
					}
				}
				
				m_makeCallback (m_request, newMat, m_matData);
			}

			bool allTexturesLoaded { get; set; }

			AssetRequest m_request;
			MaterialData m_matData;
			Action<AssetRequest, Material, MaterialData> m_makeCallback;
		}

		IDictionary<uint, MaterialMaker> m_dependencyRequesters = new Dictionary<uint, MaterialMaker> ();

		IDictionary<uint, MaterialData> m_cachedAssetsDepends = new Dictionary<uint, MaterialData> ();
	}
}
